package stv5.old;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import stv5.Episode;
import stv5.Request;
import stv5.Series;
//import stv5.abstracted.EpisodeManager;

/**
 * A manager that uses mediatomb to stream videos. If run
 *   in compatibleMode (IE: By passing at least one string arg)
 *   it will ignore guids in the DB, and spider on startup to 
 *   find series based on name.
 *   
 * Compatibility Arguments:
 * 	Start - This is the "start" Folder for spidering; this may
 * 	be simply an empty string
 *  
 * @author dhleong
 *
 */
@Deprecated
public class Old_MediaTombManager { //implements EpisodeManager {
	private static final int SID_WAIT = 5000;
	private static final int UPDATE_WAIT = 2500;
	private String broadIp;
	private int mtPort;
	private HashMap<Integer, ArrayList<Episode>> episodes;
	private String[] localFolders;
	
	private String sid;
	private boolean compatibleMode=false;
	private String[] compatArgs;
	private HashMap<String, String> seriesNameToId;

	public Old_MediaTombManager(String broadcastingIp, int mtport) {
		this.broadIp = broadcastingIp;
		this.mtPort = mtport;
	}
	
	public Old_MediaTombManager(String broadcastingIp, int mtport, String...compatArgs) {
		this(broadcastingIp, mtport);
		this.compatibleMode = true;
		this.compatArgs = compatArgs;
	}

	public void appendSettingsList(StringBuffer buff) {
		// no additional settings for now
	}
	
	private boolean doSleep(int millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {}
		
		return true;
	}

	public List<Episode> getEpisodes(int seriesId) {
		return episodes.get(seriesId);
	}

	public String getLocalForEpisode(Series s, Episode e) {
		String path = null, series, link;
		File theFile;
		for (String root : localFolders) {
			link = e.getName();			
			series = s.getName().replace(" - ", File.separator);
		
			path = root+File.separator+series+File.separator+link;			
			theFile = new File(path);
			if (theFile.exists()) 
				return theFile.getAbsolutePath();
		}
		
		return null;
	}

	public void handleRequest(StringBuffer buff, Request request) {
		// no additional settings for now
	}
	
	/**
	 * @return True if the MediaTomb is updating its database 
	 */
	public boolean isUpdating() {
		try {
			Document doc = loadPage("update&force_update=0");
			NodeList tasklist = doc.getElementsByTagName("task");
			return (tasklist.getLength() > 0);			
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
		}
		return false;
	}

	public void manageSeries(Series s) {
		String guid = (compatibleMode) ? seriesNameToId.get(s.getName().toLowerCase()) : s.getGuid();
		
		// load episodes directly		
		episodes.put(s.getId(), episodesForContainer(guid));		
	}

	public void reload() {
		episodes = new HashMap<Integer, ArrayList<Episode>>();
		seriesNameToId = new HashMap<String, String>();
		
		// get a new SID
		if (!refreshSID()) {
			System.out.println (" * SID Refresh, attempt #2 in ~5 sec");
			if (doSleep(SID_WAIT) && !refreshSID()) {
				System.out.println(" * SID Refresh, attempt #3 in ~5 sec");
				if (doSleep(SID_WAIT) && !refreshSID()) {
					System.out.println(" * Failed to refresh SID after 3 attempts!");
					System.out.println(" * You may want to hard-restart STv5!");
				}
			}
		}
		// wait until it's done updating
		if (isUpdating()) {
			System.out.print(" - Waiting for MediaTomb to finish updating");
			do {
				doSleep(UPDATE_WAIT);
				System.out.print(".");
			} while (isUpdating());
			System.out.println(" Done.");
		}
		
		// find local folders from the "autoscan" list
		try {
			Document localdoc = loadPage("autoscan&action=list");
			NodeList loclist = localdoc.getElementsByTagName("location");
			localFolders = new String[ loclist.getLength() ];
			for (int i=0; i<loclist.getLength(); i++) 
				localFolders[i] = loclist.item(i).getTextContent();				
			
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		// if in compatibility mode, pre-scan folders to locate ids
		if (compatibleMode) {
			// A bit ugly, but we need the case insensitivity. This IS
			// "compatibility" mode, after all!
			for (Entry<String, String> e : spiderFolders(compatArgs[0]).entrySet()) 
				seriesNameToId.put( e.getKey().toLowerCase(), e.getValue() );
		}
	}

	/**
	 * @return True on success, else false
	 */
	private boolean refreshSID() {
		// get an sid for mediatomb
		try {
			Document siddoc = loadPage("auth&checkSID=1");
			NodeList rootlist = siddoc.getElementsByTagName("root");
			Node rootNode = rootlist.item(0);			
			sid = rootNode.getAttributes().getNamedItem("sid").getNodeValue();			
		} catch (SAXException e) {
			e.printStackTrace();
			return false;
		} catch (IOException e) {
			//e.printStackTrace();
			System.out.println("Could not connect to mediatomb server. Failed.");
			System.exit(1);		
			return false;
		} catch (NullPointerException e) {
			System.out.println(" * Could not retrieve a new SID.");
			return false;
		}
		
		return true;
	}

	public int size() {
		return episodes.size();
	}

	public HashMap<String, String> spiderFolders() {
		return doSpiderFolders("1", "");
	}

	public HashMap<String, String> spiderFolders(String folderName) {
		HashMap<String, String> ret = new HashMap<String,String>();
		int pos=0;
		String name, search = folderName + " - ";		
		for (Entry<String,String> e : spiderFolders().entrySet()) {
			if (e.getKey().equals(folderName))
				continue;			
			pos = e.getKey().indexOf(search);
			if (pos == -1) // if it doesn't have the folder name at all, we don't want it!
				continue;			
			name = (pos > -1) ? e.getKey().substring(pos+search.length()) : e.getKey();			
			ret.put(name, e.getValue());
		}
		return ret;
	}
	
	/**
	 * This is so we can get the nice "Series - Season 2" or whatever name structure
	 * 	from nested folders
	 * 
	 * We pass the current name to the recursive function, because there's no way to
	 * 	get the name of the current folder from the "container" results in mediatomb 
	 * 
	 * @param containerId
	 * @param parentsName
	 * @return
	 */
	private HashMap<String, String> doSpiderFolders(String containerId, String currentName) {
		ArrayList<Episode> currEpisodes;
		HashMap<String, String> ret = new HashMap<String, String>();
		
		currEpisodes = episodesForContainer(containerId);
		if (!currEpisodes.isEmpty())
			ret.put(currentName, containerId);
		
		try {
			// recurse into subdirectories
			Document cont = loadPage("containers&parent_id="+containerId);
			NodeList nodes = cont.getElementsByTagName("container");
			Node curr;
            for (int i=0; i<nodes.getLength(); i++) {
            	curr = nodes.item(i);

        		// only extend the series name if this one has episodes in it
        		String newname = (!currEpisodes.isEmpty()) 
        			? currentName + " - " + curr.getTextContent()
        			: curr.getTextContent();        			
        			
        		// add the recursion results
        		for (Entry<String,String> e : doSpiderFolders(curr.getAttributes().getNamedItem("id").getNodeValue(), 
        				capitalizeWords(newname)).entrySet()) {
        			ret.put(e.getKey(), e.getValue());
        		}            	
            }
		} catch (SAXException e) {
		} catch (IOException e) {
		}
		
		return ret;
	}
	
	/**
	 * @param containerId
	 * @return empty list on failure
	 */
	private ArrayList<Episode> episodesForContainer(String containerId) {
		ArrayList<Episode> episodes = new ArrayList<Episode>();
		try {			
			Document cont = loadPage("items&parent_id="+containerId);
			NodeList items = cont.getElementsByTagName("item");
			if (items == null || items.getLength() == 0)
				return episodes;
			NodeList children;
			String link;			
			for (int i=0; i<items.getLength(); i++) {
				children = items.item(i).getChildNodes();
				// "title" (filename) is first child
				if (isVideoFile(children.item(0).getTextContent())) {
					// "res" (the link) is second child
					link = children.item(1).getTextContent().replace("res_id=0", "res_id=none")
							+"&pr_name=video-common&tr=1";					
					episodes.add(new Episode(
							children.item(0).getTextContent(),
							link
					));
					
				}
			}
		} catch (SAXException e) {
		} catch (IOException e) {
		}
		
		Collections.sort(episodes);
		return episodes;
	}

	/**
	 * @param url, starting with the req_type=
	 * 	eg: "containers&parent_id=0"
	 * @return
	 * @throws SAXException
	 * @throws IOException
	 */
	private Document loadPage(String url) throws SAXException, IOException {		
		DOMParser parser = new DOMParser();
		parser.parse("http://"+broadIp+":"+mtPort+"/content/interface?sid="+sid+"&req_type="+url);
		Document doc = parser.getDocument();
		return doc;
	}
	
	/**
	 * TODO: Move the following into an abstract Manager class
	 */
	public static boolean isVideoFile(String filename) {
		return filename.endsWith(".avi")
				|| filename.endsWith(".ogm")
				|| filename.endsWith(".mkv")
				|| filename.endsWith(".mpg")
				|| filename.endsWith(".mpeg")
				|| filename.endsWith(".mp4");
	}
	
	public static String capitalizeWords(String str) {
		String[] words = str.split("\\s");
		String firstLetter;
		StringBuffer newname = new StringBuffer();
		for (int i=0; i<words.length; i++) {
			firstLetter = words[i].substring(0, 1);		
			try {				
				newname.append( words[i].replaceFirst(firstLetter, firstLetter.toUpperCase()) );
			} catch (Exception e) {}
			// just ignore the exception; it means the first character
			//  was not a letter
			if (i < words.length-1) newname.append(' ');				
		}	
		
		return newname.toString();
	}
}
